﻿using System;
using System.Configuration;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

namespace Red.Controls.Utils
{
    public static class W32
    {
        #region W32 data types

        [Serializable]
        [StructLayout( LayoutKind.Sequential )]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;

            public RECT( int left, int top, int right, int bottom )
            {
                Left = left;
                Top = top;
                Right = right;
                Bottom = bottom;
            }
        }

        [Serializable]
        [StructLayout( LayoutKind.Sequential )]
        public struct POINT
        {
            public int X;
            public int Y;

            public POINT( int x, int y )
            {
                X = x;
                Y = y;
            }
        }

        [Serializable]
        [StructLayout( LayoutKind.Sequential )]
        public struct WINDOWPLACEMENT
        {
            public int length;
            public int flags;
            public int showCmd;
            public POINT minPosition;
            public POINT maxPosition;
            public RECT normalPosition;
        }

        [StructLayout( LayoutKind.Sequential, Pack = 4 )]
        public struct MONITORINFO
        {
            public int cbSize;
            public RECT rcMonitor;
            public RECT rcWork;
            public int dwFlags;
        }

        #endregion W32 data types

        #region Window placement support

        [DllImport( "user32.dll" )]
        public static extern bool SetWindowPlacement( IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl );

        [DllImport( "user32.dll" )]
        public static extern bool GetWindowPlacement( IntPtr hWnd, out WINDOWPLACEMENT lpwndpl );

        public const int SW_SHOWNORMAL = 1;
        public const int SW_SHOWMINIMIZED = 2;

        #endregion Window placement support

        #region Monitor support

        [DllImport( "user32.dll" )]
        public static extern IntPtr MonitorFromRect( [In] ref RECT rect, int flags );

        [DllImport( "user32.dll" )]
        public static extern bool GetMonitorInfo( IntPtr hMonitor, out MONITORINFO lpmi );

        public const int MONITOR_DEFAULTTONULL = 0;
        public const int MONITOR_DEFAULTTOPRIMARY = 1;
        public const int MONITOR_DEFAULTTONEAREST = 2;

        #endregion Monitor support
    }

    public static class WindowPositionSaver
    {
        /// <summary>
        /// Persist window placement details to application settings.
        /// Requires property WindowPlacement of type Red.Controls.WINDOWPLACEMENT to exist in settings.
        /// </summary> 
        public static void Save( Window wnd, ApplicationSettingsBase settings )
        {
            IntPtr hwnd = new WindowInteropHelper( wnd ).Handle;
            W32.WINDOWPLACEMENT wp;
            W32.GetWindowPlacement( hwnd, out wp );
            settings[ "WindowPlacement" ] = wp;
            settings[ "WindowPlacementSaved" ] = true;
        }

        /// <summary>
        /// Load window placement details for previous application session from application settings
        /// Requires property WindowPlacement of type Red.Controls.WINDOWPLACEMENT to exist in settings.
        /// </summary> 
        public static void Load( Window wnd, ApplicationSettingsBase settings )
        {
            bool saved = false;
            try
            {
                saved = (bool)settings[ "WindowPlacementSaved" ];
            }
            catch ( NullReferenceException )
            {
            }

            // Don't load window placement data if nothing was saved
            if ( !saved )
                return;

            IntPtr hwnd = new WindowInteropHelper( wnd ).Handle;
            W32.WINDOWPLACEMENT wp;
            try
            {
                wp = (W32.WINDOWPLACEMENT)settings[ "WindowPlacement" ];
            }
            catch ( NullReferenceException )
            {
                W32.GetWindowPlacement( hwnd, out wp );
            }

            if ( wnd.ResizeMode == ResizeMode.CanMinimize )
            {
                wnd.Left = wp.normalPosition.Left;
                wnd.Top = wp.normalPosition.Top;

                if ( wp.showCmd == W32.SW_SHOWMINIMIZED )
                {
                    wnd.WindowState = WindowState.Minimized;
                }
            }
            else if ( wnd.ResizeMode == ResizeMode.NoResize )
            {
                wnd.Left = wp.normalPosition.Left;
                wnd.Top = wp.normalPosition.Top;
            }
            else
            {
                wp.flags = 0;
                wp.showCmd = ( wp.showCmd == W32.SW_SHOWMINIMIZED ? W32.SW_SHOWNORMAL : wp.showCmd );
                W32.SetWindowPlacement( hwnd, ref wp );
            }
        }
    }

    public static class MonitorUtils
    {
        /// <summary>
        ///     For given window, returns the bounds of a screen the window is on
        /// </summary>
        public static Rect GetMonitorWorkingSizeFromWindow( Window wnd )
        {
            IntPtr hwnd = new WindowInteropHelper( wnd ).Handle;
            var rect = new W32.RECT( (int)wnd.Left, (int)wnd.Top, (int)( wnd.Left + wnd.Width ), (int)( wnd.Top + wnd.Height ) );
            IntPtr monitor = W32.MonitorFromRect( ref rect, W32.MONITOR_DEFAULTTONEAREST );

            W32.MONITORINFO mi;
            mi.cbSize = Marshal.SizeOf( typeof( W32.MONITORINFO ) );
            W32.GetMonitorInfo( monitor, out mi );

            return new Rect
            (
                mi.rcWork.Left,
                mi.rcWork.Top,
                mi.rcWork.Right - mi.rcWork.Left - 1,
                mi.rcWork.Bottom - mi.rcWork.Top - 1
            );
        }
    }
}
